{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": 4,
      "metadata": {
        "id": "6fOOS2iWTp3H"
      },
      "outputs": [],
      "source": [
        "%%capture --no-stderr\n",
        "%pip install --quiet -U langgraph langchain_openai langchain_google_genai python-dotenv"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "metadata": {
        "id": "WVbTUQ9q0m1d"
      },
      "outputs": [],
      "source": [
        "import os\n",
        "from google.colab import userdata\n",
        "\n",
        "os.environ[\"OPENAI_API_KEY\"] = userdata.get('OPENAI_API_KEY')\n",
        "os.environ[\"GOOGLE_API_KEY\"] = userdata.get('GOOGLE_API_KEY')\n",
        "\n",
        "os.environ[\"LANGCHAIN_API_KEY\"] = userdata.get('LANGCHAIN_API_KEY')\n",
        "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
        "os.environ[\"LANGCHAIN_PROJECT\"] = \"langchain-functional-api\""
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "from langchain_google_genai import ChatGoogleGenerativeAI\n",
        "\n",
        "model = ChatGoogleGenerativeAI(model=\"gemini-2.0-flash-exp\")"
      ],
      "metadata": {
        "id": "3EtakjdImZQd"
      },
      "execution_count": 5,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Poem Flow"
      ],
      "metadata": {
        "id": "Ny7wLysb8ef2"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "### 1. Synchronous"
      ],
      "metadata": {
        "id": "e2Gi_tsHHN8P"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "import os\n",
        "from random import randint\n",
        "from langgraph.func import entrypoint, task\n",
        "from langchain_google_genai import ChatGoogleGenerativeAI\n",
        "\n",
        "# Initialize the AI Model (Poem Generation)\n",
        "model = ChatGoogleGenerativeAI(model=\"gemini-2.0-flash-exp\")\n",
        "\n",
        "@task\n",
        "def generate_sentence_count() -> int:\n",
        "    \"\"\"Generate a random sentence count for the poem.\"\"\"\n",
        "    return randint(1, 5)\n",
        "\n",
        "@task\n",
        "def generate_poem(sentence_count: int) -> str:\n",
        "    \"\"\"Generate a poem based on the sentence count using the AI model.\"\"\"\n",
        "    prompt = f\"Write a beautiful and engaging poem about CrewAI with exactly {sentence_count} sentences.\"\n",
        "    response = model.invoke(prompt)\n",
        "    return response.content.strip()\n",
        "\n",
        "@task\n",
        "def save_poem(poem: str) -> str:\n",
        "    \"\"\"Save the poem to a file in a correct directory to avoid path errors.\"\"\"\n",
        "    output_dir = \"output\"\n",
        "    os.makedirs(output_dir, exist_ok=True)  # Ensure the directory exists\n",
        "    file_path = os.path.join(output_dir, \"poem.txt\")\n",
        "\n",
        "    with open(file_path, \"w\", encoding=\"utf-8\") as f:\n",
        "        f.write(poem)\n",
        "\n",
        "    return f\"Poem saved successfully at {file_path}\"\n",
        "\n",
        "@entrypoint()\n",
        "def run_workflow(input: str | None):\n",
        "    \"\"\"Workflow to generate and save a poem.\"\"\"\n",
        "    sentence_count = generate_sentence_count().result()\n",
        "    poem = generate_poem(sentence_count).result()\n",
        "    save_status = save_poem(poem).result()\n",
        "\n",
        "    return {\"sentence_count\": sentence_count, \"poem\": poem, \"status\": save_status}\n",
        "\n",
        "def stream():\n",
        "    for event in run_workflow.stream(input=\"\"):\n",
        "        print(event)"
      ],
      "metadata": {
        "id": "1zu8IfNE8g2V"
      },
      "execution_count": 38,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "stream()"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "TKAUismX9nH1",
        "outputId": "aa8d645d-0589-41a5-c19c-0d86344511d5"
      },
      "execution_count": 39,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "{'generate_sentence_count': 2}\n",
            "{'generate_poem': 'With digital minds as its crew, CrewAI embarks on quests, weaving intricate tasks into seamless, collaborative flows, a symphony of agents bringing innovation to life.\\nThe team, like skilled navigators, charts courses through complex problems, crafting solutions with intelligence and grace, a testament to the power of collaborative AI.'}\n",
            "{'save_poem': 'Poem saved successfully at output/poem.txt'}\n",
            "{'run_workflow': {'sentence_count': 2, 'poem': 'With digital minds as its crew, CrewAI embarks on quests, weaving intricate tasks into seamless, collaborative flows, a symphony of agents bringing innovation to life.\\nThe team, like skilled navigators, charts courses through complex problems, crafting solutions with intelligence and grace, a testament to the power of collaborative AI.', 'status': 'Poem saved successfully at output/poem.txt'}}\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "### 2. Asynchronous"
      ],
      "metadata": {
        "id": "j56bjHzBHa5n"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "import os\n",
        "from random import randint\n",
        "from langgraph.func import entrypoint, task\n",
        "from langchain_google_genai import ChatGoogleGenerativeAI\n",
        "\n",
        "@task\n",
        "async def generate_sentence_count() -> int:\n",
        "    \"\"\"Generate a random sentence count for the poem.\"\"\"\n",
        "    return randint(1, 5)\n",
        "\n",
        "@task\n",
        "async def generate_poem(sentence_count: int) -> str:\n",
        "    \"\"\"Generate a poem based on the sentence count using the AI model.\"\"\"\n",
        "    prompt = f\"Write a beautiful and engaging poem about AI Agents with exactly {sentence_count} sentences.\"\n",
        "    response = await model.ainvoke(prompt)\n",
        "    return response.content.strip()\n",
        "\n",
        "@task\n",
        "async def save_poem(poem: str) -> str:\n",
        "    \"\"\"Save the poem to a file in a correct directory to avoid path errors.\"\"\"\n",
        "    output_dir = \"output\"\n",
        "    os.makedirs(output_dir, exist_ok=True)  # Ensure the directory exists\n",
        "    file_path = os.path.join(output_dir, \"poem.txt\")\n",
        "\n",
        "    with open(file_path, \"w\", encoding=\"utf-8\") as f:\n",
        "        f.write(poem)\n",
        "\n",
        "    return f\"Poem saved successfully at {file_path}\"\n",
        "\n",
        "@entrypoint()\n",
        "async def run_workflow(input: str | None):\n",
        "    \"\"\"Workflow to generate and save a poem.\"\"\"\n",
        "    sentence_count = await generate_sentence_count()\n",
        "    poem = await generate_poem(sentence_count)\n",
        "    save_status = await save_poem(poem)\n",
        "\n",
        "    return {\"sentence_count\": sentence_count, \"poem\": poem, \"status\": save_status}\n",
        "\n",
        "async def stream():\n",
        "    async for event in run_workflow.astream(input=\"\"):\n",
        "        print(event)"
      ],
      "metadata": {
        "id": "r3hkSvvl-8NS"
      },
      "execution_count": 41,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "await stream()"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "we4g4EuJHmzn",
        "outputId": "8b97c981-aeb1-4535-e60b-bf11fe85dc4a"
      },
      "execution_count": 42,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "{'generate_sentence_count': 1}\n",
            "{'generate_poem': \"Emerging from code's deep well, AI agents weave through the digital realm, their minds a symphony of algorithms, learning, adapting, and transforming, as they explore, connect, and create new possibilities, a testament to human ingenuity reaching beyond the bounds of flesh and bone.\"}\n",
            "{'save_poem': 'Poem saved successfully at output/poem.txt'}\n",
            "{'run_workflow': {'sentence_count': 1, 'poem': \"Emerging from code's deep well, AI agents weave through the digital realm, their minds a symphony of algorithms, learning, adapting, and transforming, as they explore, connect, and create new possibilities, a testament to human ingenuity reaching beyond the bounds of flesh and bone.\", 'status': 'Poem saved successfully at output/poem.txt'}}\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "----\n",
        "----\n",
        "> **The Concepts Below are Optional for AI-202 students go through.**\n",
        "----"
      ],
      "metadata": {
        "id": "wDDgNcxiJJOS"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Human in the Loop (Hello)\n",
        "\n",
        "https://langchain-ai.github.io/langgraph/how-tos/wait-user-input-functional/?h=functiona"
      ],
      "metadata": {
        "id": "Bgu_SlbaIShT"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "import time\n",
        "import uuid\n",
        "\n",
        "from langgraph.func import entrypoint, task\n",
        "from langgraph.types import Command, interrupt\n",
        "\n",
        "\n",
        "@task\n",
        "def step_1(input_query):\n",
        "    \"\"\"Append bar.\"\"\"\n",
        "    return f\"{input_query} bar\"\n",
        "\n",
        "\n",
        "@task\n",
        "def human_feedback(input_query):\n",
        "    \"\"\"Append user input.\"\"\"\n",
        "    feedback = interrupt(f\"Please provide feedback: {input_query}\")\n",
        "    return f\"{input_query} {feedback}\"\n",
        "\n",
        "\n",
        "@task\n",
        "def step_3(input_query):\n",
        "    \"\"\"Append qux.\"\"\"\n",
        "    return f\"{input_query} qux\""
      ],
      "metadata": {
        "id": "EFa7u4WoIDZ1"
      },
      "execution_count": 52,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "from langgraph.checkpoint.memory import MemorySaver\n",
        "\n",
        "checkpointer = MemorySaver()\n",
        "\n",
        "\n",
        "@entrypoint(checkpointer=checkpointer)\n",
        "def graph(input_query):\n",
        "    result_1 = step_1(input_query).result()\n",
        "    result_2 = human_feedback(result_1).result()\n",
        "    result_3 = step_3(result_2).result()\n",
        "\n",
        "    return result_3"
      ],
      "metadata": {
        "id": "Ip9cld9mJz3d"
      },
      "execution_count": 54,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "thread_id = str(uuid.uuid4())\n",
        "\n",
        "config = {\n",
        "    \"configurable\": {\n",
        "        \"thread_id\": thread_id\n",
        "    }\n",
        "}\n",
        "\n",
        "for item in graph.stream(\"AI Agents\", config):\n",
        "    print(item)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "IJMb-ypUJypk",
        "outputId": "08dab74f-a1c2-4ed4-e9bd-b5db1e51f6c4"
      },
      "execution_count": 57,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "{'step_1': 'AI Agents bar'}\n",
            "{'__interrupt__': (Interrupt(value='Please provide feedback: AI Agents bar', resumable=True, ns=['graph:70dbb4c5-af34-bb93-2442-d0c244bca804', 'human_feedback:bd5aafb2-c018-828a-5481-eb7fbd30d55b'], when='during'),)}\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "# Get review from a user (e.g., via a UI)\n",
        "# In this case, we're using a bool, but this can be any json-serializable value.\n",
        "human_review = \"proceed - send them to planner for this concept implementation\"\n",
        "\n",
        "for item in graph.stream(Command(resume=human_review), config):\n",
        "    print(item)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "Kj_DZYhZIPU-",
        "outputId": "a9462633-a185-4677-8fe0-172baa01ab98"
      },
      "execution_count": 58,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "{'human_feedback': 'AI Agents bar proceed - send them to planner for this concept implementation'}\n",
            "{'step_3': 'AI Agents bar proceed - send them to planner for this concept implementation qux'}\n",
            "{'graph': 'AI Agents bar proceed - send them to planner for this concept implementation qux'}\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Persistance (Short Term Memory)\n",
        "\n",
        "https://langchain-ai.github.io/langgraph/how-tos/persistence-functional/?h=functiona\n",
        "\n",
        "https://langchain-ai.github.io/langgraph/how-tos/cross-thread-persistence-functional/?h=functiona"
      ],
      "metadata": {
        "id": "sjF7iEKSYsEO"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "import time\n",
        "import uuid\n",
        "from langgraph.func import entrypoint, task\n",
        "from langgraph.checkpoint.memory import MemorySaver\n",
        "\n",
        "# Setting up persistence\n",
        "checkpointer = MemorySaver()\n",
        "\n",
        "@task\n",
        "def generate_city(country: str) -> str:\n",
        "    \"\"\"Generate a random city using an LLM call.\"\"\"\n",
        "\n",
        "    print(\"Starting flow\")\n",
        "    response = model.invoke(f\"Return the name of a random city in the {country}.\")\n",
        "    random_city = response.content\n",
        "    print(f\"Random City: {random_city}\")\n",
        "    return random_city\n",
        "\n",
        "@task\n",
        "def generate_fun_fact(city: str) -> str:\n",
        "    \"\"\"Generate a fun fact about the given city.\"\"\"\n",
        "\n",
        "    response = model.invoke(f\"Tell me a fun fact about {city}\")\n",
        "    fun_fact = response.content\n",
        "    return fun_fact\n",
        "\n",
        "@entrypoint(checkpointer=checkpointer)\n",
        "def main_workflow(country: str) -> dict:\n",
        "    \"\"\"Main workflow that generates a random city and fetches a fun fact about it.\"\"\"\n",
        "    city = generate_city(country).result()\n",
        "    fact = generate_fun_fact(city).result()\n",
        "\n",
        "    return {\"city\": city, \"fun_fact\": fact, \"country\": country}\n",
        "\n",
        "\n",
        "thread_id = str(uuid.uuid4())\n",
        "\n",
        "config = {\n",
        "    \"configurable\": {\n",
        "        \"thread_id\": thread_id\n",
        "    }\n",
        "}"
      ],
      "metadata": {
        "id": "a2fekrmUV9-q"
      },
      "execution_count": 45,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "for result in main_workflow.stream(\"Pakistan\", config):\n",
        "  print(f\"Generated fun fact: {result}\")"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "Ecm6ypJsXQIH",
        "outputId": "5bac68e9-b9cc-4a9b-e4c2-b40c0400e32a"
      },
      "execution_count": 46,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Starting flow\n",
            "Random City: Okay, here's a random city in Pakistan:\n",
            "\n",
            "**Faisalabad**\n",
            "Generated fun fact: {'generate_city': \"Okay, here's a random city in Pakistan:\\n\\n**Faisalabad**\"}\n",
            "Generated fun fact: {'generate_fun_fact': 'Okay, here\\'s a fun fact about Faisalabad, Pakistan:\\n\\n**Faisalabad is known as the \"Manchester of Pakistan\" due to its significant contribution to the country\\'s textile industry.** Just like Manchester in England was a major hub for textiles during the Industrial Revolution, Faisalabad is a powerhouse for textile production in Pakistan, with numerous mills and factories dedicated to spinning, weaving, and processing fabrics.'}\n",
            "Generated fun fact: {'main_workflow': {'city': \"Okay, here's a random city in Pakistan:\\n\\n**Faisalabad**\", 'fun_fact': 'Okay, here\\'s a fun fact about Faisalabad, Pakistan:\\n\\n**Faisalabad is known as the \"Manchester of Pakistan\" due to its significant contribution to the country\\'s textile industry.** Just like Manchester in England was a major hub for textiles during the Industrial Revolution, Faisalabad is a powerhouse for textile production in Pakistan, with numerous mills and factories dedicated to spinning, weaving, and processing fabrics.', 'country': 'Pakistan'}}\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "main_workflow.get_state(config)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "wWrTaBRIIe_8",
        "outputId": "0fa6b68a-0e11-4d39-a116-f6d50e1a9ca2"
      },
      "execution_count": 47,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "StateSnapshot(values={'city': \"Okay, here's a random city in Pakistan:\\n\\n**Faisalabad**\", 'fun_fact': 'Okay, here\\'s a fun fact about Faisalabad, Pakistan:\\n\\n**Faisalabad is known as the \"Manchester of Pakistan\" due to its significant contribution to the country\\'s textile industry.** Just like Manchester in England was a major hub for textiles during the Industrial Revolution, Faisalabad is a powerhouse for textile production in Pakistan, with numerous mills and factories dedicated to spinning, weaving, and processing fabrics.', 'country': 'Pakistan'}, next=(), config={'configurable': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'checkpoint_ns': '', 'checkpoint_id': '1efe2385-8b6c-6897-8000-f15a44655627'}}, metadata={'source': 'loop', 'writes': {'main_workflow': {'city': \"Okay, here's a random city in Pakistan:\\n\\n**Faisalabad**\", 'fun_fact': 'Okay, here\\'s a fun fact about Faisalabad, Pakistan:\\n\\n**Faisalabad is known as the \"Manchester of Pakistan\" due to its significant contribution to the country\\'s textile industry.** Just like Manchester in England was a major hub for textiles during the Industrial Revolution, Faisalabad is a powerhouse for textile production in Pakistan, with numerous mills and factories dedicated to spinning, weaving, and processing fabrics.', 'country': 'Pakistan'}, 'generate_city': \"Okay, here's a random city in Pakistan:\\n\\n**Faisalabad**\", 'generate_fun_fact': 'Okay, here\\'s a fun fact about Faisalabad, Pakistan:\\n\\n**Faisalabad is known as the \"Manchester of Pakistan\" due to its significant contribution to the country\\'s textile industry.** Just like Manchester in England was a major hub for textiles during the Industrial Revolution, Faisalabad is a powerhouse for textile production in Pakistan, with numerous mills and factories dedicated to spinning, weaving, and processing fabrics.'}, 'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'step': 0, 'parents': {}}, created_at='2025-02-03T14:08:30.619429+00:00', parent_config={'configurable': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'checkpoint_ns': '', 'checkpoint_id': '1efe2385-7d06-6320-bfff-8ea9987e6def'}}, tasks=())"
            ]
          },
          "metadata": {},
          "execution_count": 47
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "result2 = main_workflow.invoke(\"cat\", config)\n",
        "print(f\"Generated fun fact: {result2}\")"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "Y1WxeG65XQ_W",
        "outputId": "5b9efc9b-2236-4193-d59e-374f190a1055"
      },
      "execution_count": 48,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Starting flow\n",
            "Random City: Okay, I understand! However, there seems to be a misunderstanding. You asked for the name of a random city \"in the cat.\" Cats are animals, and they don't have cities. \n",
            "\n",
            "Perhaps you meant something else? Here are some possibilities and how I can help:\n",
            "\n",
            "**Possible Interpretations and How I Can Help:**\n",
            "\n",
            "1. **Random City in the World:** If you just want a random city from anywhere, I can easily do that! \n",
            "   * **Example:** \"Okay, here's a random city: **Tokyo**.\"\n",
            "\n",
            "2. **Random City in a Specific Country/Region:** If you're thinking of a specific place, like a country or continent, I can pick a random city from that area.\n",
            "   * **Example:** \"Give me a random city in Europe.\" then I would say something like \"Okay, here's a random city in Europe: **Rome**.\"\n",
            "\n",
            "3. **A City With a Cat Theme:** Perhaps you were looking for a city that has some kind of cat-related association (a cat cafe, cat statues, etc.)? While I can't guarantee a perfect \"cat city\", I could try my best to find one.\n",
            "   * **Example:** \"I'd like a city with some cat connection.\" Then I might say something like \"Okay, how about **Kuching, Malaysia**, sometimes referred to as 'Cat City' due to many cat statues and a cat museum.\"\n",
            "\n",
            "4. **You Meant Something Else Entirely:** If it's none of the above, please clarify what you meant by \"in the cat\" and I'll do my best to understand!\n",
            "\n",
            "**Please tell me which of these you meant, or if it's something else, let me know!** I'm ready to help you find a random city.\n",
            "Generated fun fact: {'city': 'Okay, I understand! However, there seems to be a misunderstanding. You asked for the name of a random city \"in the cat.\" Cats are animals, and they don\\'t have cities. \\n\\nPerhaps you meant something else? Here are some possibilities and how I can help:\\n\\n**Possible Interpretations and How I Can Help:**\\n\\n1. **Random City in the World:** If you just want a random city from anywhere, I can easily do that! \\n   * **Example:** \"Okay, here\\'s a random city: **Tokyo**.\"\\n\\n2. **Random City in a Specific Country/Region:** If you\\'re thinking of a specific place, like a country or continent, I can pick a random city from that area.\\n   * **Example:** \"Give me a random city in Europe.\" then I would say something like \"Okay, here\\'s a random city in Europe: **Rome**.\"\\n\\n3. **A City With a Cat Theme:** Perhaps you were looking for a city that has some kind of cat-related association (a cat cafe, cat statues, etc.)? While I can\\'t guarantee a perfect \"cat city\", I could try my best to find one.\\n   * **Example:** \"I\\'d like a city with some cat connection.\" Then I might say something like \"Okay, how about **Kuching, Malaysia**, sometimes referred to as \\'Cat City\\' due to many cat statues and a cat museum.\"\\n\\n4. **You Meant Something Else Entirely:** If it\\'s none of the above, please clarify what you meant by \"in the cat\" and I\\'ll do my best to understand!\\n\\n**Please tell me which of these you meant, or if it\\'s something else, let me know!** I\\'m ready to help you find a random city.', 'fun_fact': 'Okay, I understand! However, there seems to be a misunderstanding. You asked for a fun fact about the phrase \"Okay, I understand! However, there seems to be a misunderstanding. You asked for the name of a random city \\'in the cat.\\' Cats are animals, and they don\\'t have cities.\" \\n\\nThe fun fact is that this is a perfect example of **how an AI model (like me!) can detect and explain a nonsensical or illogical request.** \\n\\nHere\\'s why it\\'s fun:\\n\\n* **It shows understanding of context:** I recognized that \"in the cat\" is not a logical place for a city.\\n* **It demonstrates problem-solving:** I didn\\'t just give a random answer; I explained the issue and offered possible alternative interpretations.\\n* **It highlights the limitations of language:** It shows how a simple phrase can be interpreted in many ways, and how AI needs to be able to understand the intent behind the words.\\n* **It\\'s a bit meta:** It\\'s a fun fact about how I, as an AI, am reacting to a misunderstanding you created!\\n\\nSo, the fun fact is that this interaction showcases my ability to identify, analyze, and correct illogical requests, which is a key aspect of how AI works.\\n\\nDo you have any other fun requests for me? Maybe this time we can explore a real city! 😉', 'country': 'cat'}\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "main_workflow.get_state(config)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "ls7Fo-HhIiva",
        "outputId": "010c6459-d9be-4c7b-e96a-e70323f2272e"
      },
      "execution_count": 49,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "StateSnapshot(values={'city': 'Okay, I understand! However, there seems to be a misunderstanding. You asked for the name of a random city \"in the cat.\" Cats are animals, and they don\\'t have cities. \\n\\nPerhaps you meant something else? Here are some possibilities and how I can help:\\n\\n**Possible Interpretations and How I Can Help:**\\n\\n1. **Random City in the World:** If you just want a random city from anywhere, I can easily do that! \\n   * **Example:** \"Okay, here\\'s a random city: **Tokyo**.\"\\n\\n2. **Random City in a Specific Country/Region:** If you\\'re thinking of a specific place, like a country or continent, I can pick a random city from that area.\\n   * **Example:** \"Give me a random city in Europe.\" then I would say something like \"Okay, here\\'s a random city in Europe: **Rome**.\"\\n\\n3. **A City With a Cat Theme:** Perhaps you were looking for a city that has some kind of cat-related association (a cat cafe, cat statues, etc.)? While I can\\'t guarantee a perfect \"cat city\", I could try my best to find one.\\n   * **Example:** \"I\\'d like a city with some cat connection.\" Then I might say something like \"Okay, how about **Kuching, Malaysia**, sometimes referred to as \\'Cat City\\' due to many cat statues and a cat museum.\"\\n\\n4. **You Meant Something Else Entirely:** If it\\'s none of the above, please clarify what you meant by \"in the cat\" and I\\'ll do my best to understand!\\n\\n**Please tell me which of these you meant, or if it\\'s something else, let me know!** I\\'m ready to help you find a random city.', 'fun_fact': 'Okay, I understand! However, there seems to be a misunderstanding. You asked for a fun fact about the phrase \"Okay, I understand! However, there seems to be a misunderstanding. You asked for the name of a random city \\'in the cat.\\' Cats are animals, and they don\\'t have cities.\" \\n\\nThe fun fact is that this is a perfect example of **how an AI model (like me!) can detect and explain a nonsensical or illogical request.** \\n\\nHere\\'s why it\\'s fun:\\n\\n* **It shows understanding of context:** I recognized that \"in the cat\" is not a logical place for a city.\\n* **It demonstrates problem-solving:** I didn\\'t just give a random answer; I explained the issue and offered possible alternative interpretations.\\n* **It highlights the limitations of language:** It shows how a simple phrase can be interpreted in many ways, and how AI needs to be able to understand the intent behind the words.\\n* **It\\'s a bit meta:** It\\'s a fun fact about how I, as an AI, am reacting to a misunderstanding you created!\\n\\nSo, the fun fact is that this interaction showcases my ability to identify, analyze, and correct illogical requests, which is a key aspect of how AI works.\\n\\nDo you have any other fun requests for me? Maybe this time we can explore a real city! 😉', 'country': 'cat'}, next=(), config={'configurable': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'checkpoint_ns': '', 'checkpoint_id': '1efe2386-0875-6f70-8002-0e74d20641f8'}}, metadata={'source': 'loop', 'writes': {'main_workflow': {'city': 'Okay, I understand! However, there seems to be a misunderstanding. You asked for the name of a random city \"in the cat.\" Cats are animals, and they don\\'t have cities. \\n\\nPerhaps you meant something else? Here are some possibilities and how I can help:\\n\\n**Possible Interpretations and How I Can Help:**\\n\\n1. **Random City in the World:** If you just want a random city from anywhere, I can easily do that! \\n   * **Example:** \"Okay, here\\'s a random city: **Tokyo**.\"\\n\\n2. **Random City in a Specific Country/Region:** If you\\'re thinking of a specific place, like a country or continent, I can pick a random city from that area.\\n   * **Example:** \"Give me a random city in Europe.\" then I would say something like \"Okay, here\\'s a random city in Europe: **Rome**.\"\\n\\n3. **A City With a Cat Theme:** Perhaps you were looking for a city that has some kind of cat-related association (a cat cafe, cat statues, etc.)? While I can\\'t guarantee a perfect \"cat city\", I could try my best to find one.\\n   * **Example:** \"I\\'d like a city with some cat connection.\" Then I might say something like \"Okay, how about **Kuching, Malaysia**, sometimes referred to as \\'Cat City\\' due to many cat statues and a cat museum.\"\\n\\n4. **You Meant Something Else Entirely:** If it\\'s none of the above, please clarify what you meant by \"in the cat\" and I\\'ll do my best to understand!\\n\\n**Please tell me which of these you meant, or if it\\'s something else, let me know!** I\\'m ready to help you find a random city.', 'fun_fact': 'Okay, I understand! However, there seems to be a misunderstanding. You asked for a fun fact about the phrase \"Okay, I understand! However, there seems to be a misunderstanding. You asked for the name of a random city \\'in the cat.\\' Cats are animals, and they don\\'t have cities.\" \\n\\nThe fun fact is that this is a perfect example of **how an AI model (like me!) can detect and explain a nonsensical or illogical request.** \\n\\nHere\\'s why it\\'s fun:\\n\\n* **It shows understanding of context:** I recognized that \"in the cat\" is not a logical place for a city.\\n* **It demonstrates problem-solving:** I didn\\'t just give a random answer; I explained the issue and offered possible alternative interpretations.\\n* **It highlights the limitations of language:** It shows how a simple phrase can be interpreted in many ways, and how AI needs to be able to understand the intent behind the words.\\n* **It\\'s a bit meta:** It\\'s a fun fact about how I, as an AI, am reacting to a misunderstanding you created!\\n\\nSo, the fun fact is that this interaction showcases my ability to identify, analyze, and correct illogical requests, which is a key aspect of how AI works.\\n\\nDo you have any other fun requests for me? Maybe this time we can explore a real city! 😉', 'country': 'cat'}, 'generate_city': 'Okay, I understand! However, there seems to be a misunderstanding. You asked for the name of a random city \"in the cat.\" Cats are animals, and they don\\'t have cities. \\n\\nPerhaps you meant something else? Here are some possibilities and how I can help:\\n\\n**Possible Interpretations and How I Can Help:**\\n\\n1. **Random City in the World:** If you just want a random city from anywhere, I can easily do that! \\n   * **Example:** \"Okay, here\\'s a random city: **Tokyo**.\"\\n\\n2. **Random City in a Specific Country/Region:** If you\\'re thinking of a specific place, like a country or continent, I can pick a random city from that area.\\n   * **Example:** \"Give me a random city in Europe.\" then I would say something like \"Okay, here\\'s a random city in Europe: **Rome**.\"\\n\\n3. **A City With a Cat Theme:** Perhaps you were looking for a city that has some kind of cat-related association (a cat cafe, cat statues, etc.)? While I can\\'t guarantee a perfect \"cat city\", I could try my best to find one.\\n   * **Example:** \"I\\'d like a city with some cat connection.\" Then I might say something like \"Okay, how about **Kuching, Malaysia**, sometimes referred to as \\'Cat City\\' due to many cat statues and a cat museum.\"\\n\\n4. **You Meant Something Else Entirely:** If it\\'s none of the above, please clarify what you meant by \"in the cat\" and I\\'ll do my best to understand!\\n\\n**Please tell me which of these you meant, or if it\\'s something else, let me know!** I\\'m ready to help you find a random city.', 'generate_fun_fact': 'Okay, I understand! However, there seems to be a misunderstanding. You asked for a fun fact about the phrase \"Okay, I understand! However, there seems to be a misunderstanding. You asked for the name of a random city \\'in the cat.\\' Cats are animals, and they don\\'t have cities.\" \\n\\nThe fun fact is that this is a perfect example of **how an AI model (like me!) can detect and explain a nonsensical or illogical request.** \\n\\nHere\\'s why it\\'s fun:\\n\\n* **It shows understanding of context:** I recognized that \"in the cat\" is not a logical place for a city.\\n* **It demonstrates problem-solving:** I didn\\'t just give a random answer; I explained the issue and offered possible alternative interpretations.\\n* **It highlights the limitations of language:** It shows how a simple phrase can be interpreted in many ways, and how AI needs to be able to understand the intent behind the words.\\n* **It\\'s a bit meta:** It\\'s a fun fact about how I, as an AI, am reacting to a misunderstanding you created!\\n\\nSo, the fun fact is that this interaction showcases my ability to identify, analyze, and correct illogical requests, which is a key aspect of how AI works.\\n\\nDo you have any other fun requests for me? Maybe this time we can explore a real city! 😉'}, 'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'step': 2, 'parents': {}}, created_at='2025-02-03T14:08:43.730485+00:00', parent_config={'configurable': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'checkpoint_ns': '', 'checkpoint_id': '1efe2385-cff7-6237-8001-586ae48c37cd'}}, tasks=())"
            ]
          },
          "metadata": {},
          "execution_count": 49
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "async for result in main_workflow.astream_events(\"Pakistan\", config, version=\"v2\"):\n",
        "  print(f\"Generated fun fact: {result}\")"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "ASkZJiOXZO1Z",
        "outputId": "a3395af1-2c06-415a-9817-c2a139b8ebd6"
      },
      "execution_count": 50,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Generated fun fact: {'event': 'on_chain_start', 'data': {'input': 'Pakistan'}, 'name': 'LangGraph', 'tags': [], 'run_id': 'f2ab0f1b-77ed-408e-bb14-df4da93c6ea0', 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055'}, 'parent_ids': []}\n",
            "Generated fun fact: {'event': 'on_chain_start', 'data': {'input': 'Pakistan'}, 'name': 'main_workflow', 'tags': ['graph:step:4'], 'run_id': 'cdcc156c-ef1b-4cbb-bfaa-66b731e75312', 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'langgraph_step': 4, 'langgraph_node': 'main_workflow', 'langgraph_triggers': ['__start__'], 'langgraph_path': ('__pregel_pull', 'main_workflow'), 'langgraph_checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3'}, 'parent_ids': ['f2ab0f1b-77ed-408e-bb14-df4da93c6ea0']}\n",
            "Generated fun fact: {'event': 'on_chain_start', 'data': {'input': {'country': 'Pakistan'}}, 'name': 'generate_city', 'tags': ['seq:step:1'], 'run_id': '0e57485d-28a6-4dd0-a1b5-82f37f45e687', 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'langgraph_step': 4, 'langgraph_node': 'generate_city', 'langgraph_triggers': ['__pregel_push'], 'langgraph_path': ('__pregel_push', ('__pregel_pull', 'main_workflow'), 0), 'langgraph_checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_city:6f47d664-d98f-7adc-2325-2e1b4b95fff8'}, 'parent_ids': ['f2ab0f1b-77ed-408e-bb14-df4da93c6ea0', 'cdcc156c-ef1b-4cbb-bfaa-66b731e75312']}\n",
            "Starting flow\n",
            "Generated fun fact: {'event': 'on_chat_model_start', 'data': {'input': {'messages': [[HumanMessage(content='Return the name of a random city in the Pakistan.', additional_kwargs={}, response_metadata={})]]}}, 'name': 'ChatGoogleGenerativeAI', 'tags': ['seq:step:1'], 'run_id': 'fac6f9e3-c7bb-434d-8867-7527cb21ae80', 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'langgraph_step': 4, 'langgraph_node': 'generate_city', 'langgraph_triggers': ['__pregel_push'], 'langgraph_path': ('__pregel_push', ('__pregel_pull', 'main_workflow'), 0), 'langgraph_checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_city:6f47d664-d98f-7adc-2325-2e1b4b95fff8', 'checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_city:6f47d664-d98f-7adc-2325-2e1b4b95fff8', 'ls_provider': 'google_genai', 'ls_model_name': 'models/gemini-2.0-flash-exp', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['f2ab0f1b-77ed-408e-bb14-df4da93c6ea0', 'cdcc156c-ef1b-4cbb-bfaa-66b731e75312', '0e57485d-28a6-4dd0-a1b5-82f37f45e687']}\n",
            "Generated fun fact: {'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='Okay', additional_kwargs={}, response_metadata={'safety_ratings': []}, id='run-fac6f9e3-c7bb-434d-8867-7527cb21ae80', usage_metadata={'input_tokens': 12, 'output_tokens': 0, 'total_tokens': 12, 'input_token_details': {'cache_read': 0}})}, 'run_id': 'fac6f9e3-c7bb-434d-8867-7527cb21ae80', 'name': 'ChatGoogleGenerativeAI', 'tags': ['seq:step:1'], 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'langgraph_step': 4, 'langgraph_node': 'generate_city', 'langgraph_triggers': ['__pregel_push'], 'langgraph_path': ('__pregel_push', ('__pregel_pull', 'main_workflow'), 0), 'langgraph_checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_city:6f47d664-d98f-7adc-2325-2e1b4b95fff8', 'checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_city:6f47d664-d98f-7adc-2325-2e1b4b95fff8', 'ls_provider': 'google_genai', 'ls_model_name': 'models/gemini-2.0-flash-exp', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['f2ab0f1b-77ed-408e-bb14-df4da93c6ea0', 'cdcc156c-ef1b-4cbb-bfaa-66b731e75312', '0e57485d-28a6-4dd0-a1b5-82f37f45e687']}\n",
            "Generated fun fact: {'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content=\", here's a random city in Pakistan:\\n\\n**Faisalabad**\", additional_kwargs={}, response_metadata={'safety_ratings': [{'category': 'HARM_CATEGORY_HATE_SPEECH', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HARASSMENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT', 'probability': 'NEGLIGIBLE', 'blocked': False}]}, id='run-fac6f9e3-c7bb-434d-8867-7527cb21ae80', usage_metadata={'input_tokens': 0, 'output_tokens': 0, 'total_tokens': 0, 'input_token_details': {'cache_read': 0}})}, 'run_id': 'fac6f9e3-c7bb-434d-8867-7527cb21ae80', 'name': 'ChatGoogleGenerativeAI', 'tags': ['seq:step:1'], 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'langgraph_step': 4, 'langgraph_node': 'generate_city', 'langgraph_triggers': ['__pregel_push'], 'langgraph_path': ('__pregel_push', ('__pregel_pull', 'main_workflow'), 0), 'langgraph_checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_city:6f47d664-d98f-7adc-2325-2e1b4b95fff8', 'checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_city:6f47d664-d98f-7adc-2325-2e1b4b95fff8', 'ls_provider': 'google_genai', 'ls_model_name': 'models/gemini-2.0-flash-exp', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['f2ab0f1b-77ed-408e-bb14-df4da93c6ea0', 'cdcc156c-ef1b-4cbb-bfaa-66b731e75312', '0e57485d-28a6-4dd0-a1b5-82f37f45e687']}\n",
            "Generated fun fact: {'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='', additional_kwargs={}, response_metadata={'finish_reason': 'STOP', 'safety_ratings': [{'category': 'HARM_CATEGORY_HATE_SPEECH', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HARASSMENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT', 'probability': 'NEGLIGIBLE', 'blocked': False}]}, id='run-fac6f9e3-c7bb-434d-8867-7527cb21ae80', usage_metadata={'input_tokens': -1, 'output_tokens': 18, 'total_tokens': 17, 'input_token_details': {'cache_read': 0}})}, 'run_id': 'fac6f9e3-c7bb-434d-8867-7527cb21ae80', 'name': 'ChatGoogleGenerativeAI', 'tags': ['seq:step:1'], 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'langgraph_step': 4, 'langgraph_node': 'generate_city', 'langgraph_triggers': ['__pregel_push'], 'langgraph_path': ('__pregel_push', ('__pregel_pull', 'main_workflow'), 0), 'langgraph_checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_city:6f47d664-d98f-7adc-2325-2e1b4b95fff8', 'checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_city:6f47d664-d98f-7adc-2325-2e1b4b95fff8', 'ls_provider': 'google_genai', 'ls_model_name': 'models/gemini-2.0-flash-exp', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['f2ab0f1b-77ed-408e-bb14-df4da93c6ea0', 'cdcc156c-ef1b-4cbb-bfaa-66b731e75312', '0e57485d-28a6-4dd0-a1b5-82f37f45e687']}\n",
            "Generated fun fact: {'event': 'on_chat_model_end', 'data': {'output': AIMessage(content=\"Okay, here's a random city in Pakistan:\\n\\n**Faisalabad**\", additional_kwargs={}, response_metadata={'safety_ratings': [{'category': 'HARM_CATEGORY_HATE_SPEECH', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HARASSMENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HATE_SPEECH', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HARASSMENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT', 'probability': 'NEGLIGIBLE', 'blocked': False}], 'finish_reason': 'STOP'}, id='run-fac6f9e3-c7bb-434d-8867-7527cb21ae80', usage_metadata={'input_tokens': 11, 'output_tokens': 18, 'total_tokens': 29, 'input_token_details': {'cache_read': 0}}), 'input': {'messages': [[HumanMessage(content='Return the name of a random city in the Pakistan.', additional_kwargs={}, response_metadata={})]]}}, 'run_id': 'fac6f9e3-c7bb-434d-8867-7527cb21ae80', 'name': 'ChatGoogleGenerativeAI', 'tags': ['seq:step:1'], 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'langgraph_step': 4, 'langgraph_node': 'generate_city', 'langgraph_triggers': ['__pregel_push'], 'langgraph_path': ('__pregel_push', ('__pregel_pull', 'main_workflow'), 0), 'langgraph_checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_city:6f47d664-d98f-7adc-2325-2e1b4b95fff8', 'checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_city:6f47d664-d98f-7adc-2325-2e1b4b95fff8', 'ls_provider': 'google_genai', 'ls_model_name': 'models/gemini-2.0-flash-exp', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['f2ab0f1b-77ed-408e-bb14-df4da93c6ea0', 'cdcc156c-ef1b-4cbb-bfaa-66b731e75312', '0e57485d-28a6-4dd0-a1b5-82f37f45e687']}\n",
            "Random City: Okay, here's a random city in Pakistan:\n",
            "\n",
            "**Faisalabad**\n",
            "Generated fun fact: {'event': 'on_chain_start', 'data': {'input': \"Okay, here's a random city in Pakistan:\\n\\n**Faisalabad**\"}, 'name': '_write', 'tags': ['seq:step:2', 'langsmith:hidden'], 'run_id': '577101b7-1395-46af-bb6e-7bafe69e30d9', 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'langgraph_step': 4, 'langgraph_node': 'generate_city', 'langgraph_triggers': ['__pregel_push'], 'langgraph_path': ('__pregel_push', ('__pregel_pull', 'main_workflow'), 0), 'langgraph_checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_city:6f47d664-d98f-7adc-2325-2e1b4b95fff8'}, 'parent_ids': ['f2ab0f1b-77ed-408e-bb14-df4da93c6ea0', 'cdcc156c-ef1b-4cbb-bfaa-66b731e75312', '0e57485d-28a6-4dd0-a1b5-82f37f45e687']}\n",
            "Generated fun fact: {'event': 'on_chain_end', 'data': {'output': \"Okay, here's a random city in Pakistan:\\n\\n**Faisalabad**\", 'input': \"Okay, here's a random city in Pakistan:\\n\\n**Faisalabad**\"}, 'run_id': '577101b7-1395-46af-bb6e-7bafe69e30d9', 'name': '_write', 'tags': ['seq:step:2', 'langsmith:hidden'], 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'langgraph_step': 4, 'langgraph_node': 'generate_city', 'langgraph_triggers': ['__pregel_push'], 'langgraph_path': ('__pregel_push', ('__pregel_pull', 'main_workflow'), 0), 'langgraph_checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_city:6f47d664-d98f-7adc-2325-2e1b4b95fff8'}, 'parent_ids': ['f2ab0f1b-77ed-408e-bb14-df4da93c6ea0', 'cdcc156c-ef1b-4cbb-bfaa-66b731e75312', '0e57485d-28a6-4dd0-a1b5-82f37f45e687']}\n",
            "Generated fun fact: {'event': 'on_chain_stream', 'run_id': '0e57485d-28a6-4dd0-a1b5-82f37f45e687', 'name': 'generate_city', 'tags': ['seq:step:1'], 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'langgraph_step': 4, 'langgraph_node': 'generate_city', 'langgraph_triggers': ['__pregel_push'], 'langgraph_path': ('__pregel_push', ('__pregel_pull', 'main_workflow'), 0), 'langgraph_checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_city:6f47d664-d98f-7adc-2325-2e1b4b95fff8'}, 'data': {'chunk': \"Okay, here's a random city in Pakistan:\\n\\n**Faisalabad**\"}, 'parent_ids': ['f2ab0f1b-77ed-408e-bb14-df4da93c6ea0', 'cdcc156c-ef1b-4cbb-bfaa-66b731e75312']}\n",
            "Generated fun fact: {'event': 'on_chain_end', 'data': {'output': \"Okay, here's a random city in Pakistan:\\n\\n**Faisalabad**\", 'input': {'country': 'Pakistan'}}, 'run_id': '0e57485d-28a6-4dd0-a1b5-82f37f45e687', 'name': 'generate_city', 'tags': ['seq:step:1'], 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'langgraph_step': 4, 'langgraph_node': 'generate_city', 'langgraph_triggers': ['__pregel_push'], 'langgraph_path': ('__pregel_push', ('__pregel_pull', 'main_workflow'), 0), 'langgraph_checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_city:6f47d664-d98f-7adc-2325-2e1b4b95fff8'}, 'parent_ids': ['f2ab0f1b-77ed-408e-bb14-df4da93c6ea0', 'cdcc156c-ef1b-4cbb-bfaa-66b731e75312']}\n",
            "Generated fun fact: {'event': 'on_chain_stream', 'run_id': 'f2ab0f1b-77ed-408e-bb14-df4da93c6ea0', 'name': 'LangGraph', 'tags': [], 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055'}, 'data': {'chunk': {'generate_city': \"Okay, here's a random city in Pakistan:\\n\\n**Faisalabad**\"}}, 'parent_ids': []}\n",
            "Generated fun fact: {'event': 'on_chain_start', 'data': {'input': {'city': None}}, 'name': 'generate_fun_fact', 'tags': ['seq:step:1'], 'run_id': 'e2408d04-634d-48cd-b417-a122298f7cb8', 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'langgraph_step': 4, 'langgraph_node': 'generate_fun_fact', 'langgraph_triggers': ['__pregel_push'], 'langgraph_path': ('__pregel_push', ('__pregel_pull', 'main_workflow'), 1), 'langgraph_checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_fun_fact:39877473-4a1c-7791-5576-966d938afda4'}, 'parent_ids': ['f2ab0f1b-77ed-408e-bb14-df4da93c6ea0', 'cdcc156c-ef1b-4cbb-bfaa-66b731e75312']}\n",
            "Generated fun fact: {'event': 'on_chat_model_start', 'data': {'input': {'messages': [[HumanMessage(content='Tell me a fun fact about None', additional_kwargs={}, response_metadata={})]]}}, 'name': 'ChatGoogleGenerativeAI', 'tags': ['seq:step:1'], 'run_id': '2549829c-dd09-4d93-8644-8f82a58000ac', 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'langgraph_step': 4, 'langgraph_node': 'generate_fun_fact', 'langgraph_triggers': ['__pregel_push'], 'langgraph_path': ('__pregel_push', ('__pregel_pull', 'main_workflow'), 1), 'langgraph_checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_fun_fact:39877473-4a1c-7791-5576-966d938afda4', 'checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_fun_fact:39877473-4a1c-7791-5576-966d938afda4', 'ls_provider': 'google_genai', 'ls_model_name': 'models/gemini-2.0-flash-exp', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['f2ab0f1b-77ed-408e-bb14-df4da93c6ea0', 'cdcc156c-ef1b-4cbb-bfaa-66b731e75312', 'e2408d04-634d-48cd-b417-a122298f7cb8']}\n",
            "Generated fun fact: {'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content='Okay', additional_kwargs={}, response_metadata={'safety_ratings': []}, id='run-2549829c-dd09-4d93-8644-8f82a58000ac', usage_metadata={'input_tokens': 8, 'output_tokens': 0, 'total_tokens': 8, 'input_token_details': {'cache_read': 0}})}, 'run_id': '2549829c-dd09-4d93-8644-8f82a58000ac', 'name': 'ChatGoogleGenerativeAI', 'tags': ['seq:step:1'], 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'langgraph_step': 4, 'langgraph_node': 'generate_fun_fact', 'langgraph_triggers': ['__pregel_push'], 'langgraph_path': ('__pregel_push', ('__pregel_pull', 'main_workflow'), 1), 'langgraph_checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_fun_fact:39877473-4a1c-7791-5576-966d938afda4', 'checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_fun_fact:39877473-4a1c-7791-5576-966d938afda4', 'ls_provider': 'google_genai', 'ls_model_name': 'models/gemini-2.0-flash-exp', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['f2ab0f1b-77ed-408e-bb14-df4da93c6ea0', 'cdcc156c-ef1b-4cbb-bfaa-66b731e75312', 'e2408d04-634d-48cd-b417-a122298f7cb8']}\n",
            "Generated fun fact: {'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content=', here\\'s a fun fact about \"None\" (in the programming sense', additional_kwargs={}, response_metadata={'safety_ratings': [{'category': 'HARM_CATEGORY_HATE_SPEECH', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HARASSMENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT', 'probability': 'NEGLIGIBLE', 'blocked': False}]}, id='run-2549829c-dd09-4d93-8644-8f82a58000ac', usage_metadata={'input_tokens': 0, 'output_tokens': 0, 'total_tokens': 0, 'input_token_details': {'cache_read': 0}})}, 'run_id': '2549829c-dd09-4d93-8644-8f82a58000ac', 'name': 'ChatGoogleGenerativeAI', 'tags': ['seq:step:1'], 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'langgraph_step': 4, 'langgraph_node': 'generate_fun_fact', 'langgraph_triggers': ['__pregel_push'], 'langgraph_path': ('__pregel_push', ('__pregel_pull', 'main_workflow'), 1), 'langgraph_checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_fun_fact:39877473-4a1c-7791-5576-966d938afda4', 'checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_fun_fact:39877473-4a1c-7791-5576-966d938afda4', 'ls_provider': 'google_genai', 'ls_model_name': 'models/gemini-2.0-flash-exp', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['f2ab0f1b-77ed-408e-bb14-df4da93c6ea0', 'cdcc156c-ef1b-4cbb-bfaa-66b731e75312', 'e2408d04-634d-48cd-b417-a122298f7cb8']}\n",
            "Generated fun fact: {'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content=', particularly in Python, but the concept applies more broadly):\\n\\n**Fun Fact', additional_kwargs={}, response_metadata={'safety_ratings': [{'category': 'HARM_CATEGORY_HATE_SPEECH', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HARASSMENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT', 'probability': 'NEGLIGIBLE', 'blocked': False}]}, id='run-2549829c-dd09-4d93-8644-8f82a58000ac', usage_metadata={'input_tokens': 0, 'output_tokens': 0, 'total_tokens': 0, 'input_token_details': {'cache_read': 0}})}, 'run_id': '2549829c-dd09-4d93-8644-8f82a58000ac', 'name': 'ChatGoogleGenerativeAI', 'tags': ['seq:step:1'], 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'langgraph_step': 4, 'langgraph_node': 'generate_fun_fact', 'langgraph_triggers': ['__pregel_push'], 'langgraph_path': ('__pregel_push', ('__pregel_pull', 'main_workflow'), 1), 'langgraph_checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_fun_fact:39877473-4a1c-7791-5576-966d938afda4', 'checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_fun_fact:39877473-4a1c-7791-5576-966d938afda4', 'ls_provider': 'google_genai', 'ls_model_name': 'models/gemini-2.0-flash-exp', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['f2ab0f1b-77ed-408e-bb14-df4da93c6ea0', 'cdcc156c-ef1b-4cbb-bfaa-66b731e75312', 'e2408d04-634d-48cd-b417-a122298f7cb8']}\n",
            "Generated fun fact: {'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content=':**  While \"None\" represents the absence of a value, it\\'s actually a *singleton object*. This means that in Python (and similar languages),', additional_kwargs={}, response_metadata={'safety_ratings': [{'category': 'HARM_CATEGORY_HATE_SPEECH', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HARASSMENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT', 'probability': 'NEGLIGIBLE', 'blocked': False}]}, id='run-2549829c-dd09-4d93-8644-8f82a58000ac', usage_metadata={'input_tokens': 0, 'output_tokens': 0, 'total_tokens': 0, 'input_token_details': {'cache_read': 0}})}, 'run_id': '2549829c-dd09-4d93-8644-8f82a58000ac', 'name': 'ChatGoogleGenerativeAI', 'tags': ['seq:step:1'], 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'langgraph_step': 4, 'langgraph_node': 'generate_fun_fact', 'langgraph_triggers': ['__pregel_push'], 'langgraph_path': ('__pregel_push', ('__pregel_pull', 'main_workflow'), 1), 'langgraph_checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_fun_fact:39877473-4a1c-7791-5576-966d938afda4', 'checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_fun_fact:39877473-4a1c-7791-5576-966d938afda4', 'ls_provider': 'google_genai', 'ls_model_name': 'models/gemini-2.0-flash-exp', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['f2ab0f1b-77ed-408e-bb14-df4da93c6ea0', 'cdcc156c-ef1b-4cbb-bfaa-66b731e75312', 'e2408d04-634d-48cd-b417-a122298f7cb8']}\n",
            "Generated fun fact: {'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content=\" there's only *one* instance of `None` in the entire program.  Every time you use `None`, you're referring to that exact\", additional_kwargs={}, response_metadata={'safety_ratings': [{'category': 'HARM_CATEGORY_HATE_SPEECH', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HARASSMENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT', 'probability': 'NEGLIGIBLE', 'blocked': False}]}, id='run-2549829c-dd09-4d93-8644-8f82a58000ac', usage_metadata={'input_tokens': 0, 'output_tokens': 0, 'total_tokens': 0, 'input_token_details': {'cache_read': 0}})}, 'run_id': '2549829c-dd09-4d93-8644-8f82a58000ac', 'name': 'ChatGoogleGenerativeAI', 'tags': ['seq:step:1'], 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'langgraph_step': 4, 'langgraph_node': 'generate_fun_fact', 'langgraph_triggers': ['__pregel_push'], 'langgraph_path': ('__pregel_push', ('__pregel_pull', 'main_workflow'), 1), 'langgraph_checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_fun_fact:39877473-4a1c-7791-5576-966d938afda4', 'checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_fun_fact:39877473-4a1c-7791-5576-966d938afda4', 'ls_provider': 'google_genai', 'ls_model_name': 'models/gemini-2.0-flash-exp', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['f2ab0f1b-77ed-408e-bb14-df4da93c6ea0', 'cdcc156c-ef1b-4cbb-bfaa-66b731e75312', 'e2408d04-634d-48cd-b417-a122298f7cb8']}\n",
            "Generated fun fact: {'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content=\" same object in memory. You can even test this with the `is` operator: `None is None` will always evaluate to `True`.\\n\\n**Why is this fun?**\\n\\n* **It's a unique individual:** Even\", additional_kwargs={}, response_metadata={'safety_ratings': [{'category': 'HARM_CATEGORY_HATE_SPEECH', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HARASSMENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT', 'probability': 'NEGLIGIBLE', 'blocked': False}]}, id='run-2549829c-dd09-4d93-8644-8f82a58000ac', usage_metadata={'input_tokens': 0, 'output_tokens': 0, 'total_tokens': 0, 'input_token_details': {'cache_read': 0}})}, 'run_id': '2549829c-dd09-4d93-8644-8f82a58000ac', 'name': 'ChatGoogleGenerativeAI', 'tags': ['seq:step:1'], 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'langgraph_step': 4, 'langgraph_node': 'generate_fun_fact', 'langgraph_triggers': ['__pregel_push'], 'langgraph_path': ('__pregel_push', ('__pregel_pull', 'main_workflow'), 1), 'langgraph_checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_fun_fact:39877473-4a1c-7791-5576-966d938afda4', 'checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_fun_fact:39877473-4a1c-7791-5576-966d938afda4', 'ls_provider': 'google_genai', 'ls_model_name': 'models/gemini-2.0-flash-exp', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['f2ab0f1b-77ed-408e-bb14-df4da93c6ea0', 'cdcc156c-ef1b-4cbb-bfaa-66b731e75312', 'e2408d04-634d-48cd-b417-a122298f7cb8']}\n",
            "Generated fun fact: {'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content=' though it represents nothing, it\\'s a very specific thing.  \\n* **Memory Efficiency:**  It\\'s more memory-efficient than creating a new \"nothing\" object each time you need one.\\n* **A subtle quirk', additional_kwargs={}, response_metadata={'safety_ratings': [{'category': 'HARM_CATEGORY_HATE_SPEECH', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HARASSMENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT', 'probability': 'NEGLIGIBLE', 'blocked': False}]}, id='run-2549829c-dd09-4d93-8644-8f82a58000ac', usage_metadata={'input_tokens': 0, 'output_tokens': 0, 'total_tokens': 0, 'input_token_details': {'cache_read': 0}})}, 'run_id': '2549829c-dd09-4d93-8644-8f82a58000ac', 'name': 'ChatGoogleGenerativeAI', 'tags': ['seq:step:1'], 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'langgraph_step': 4, 'langgraph_node': 'generate_fun_fact', 'langgraph_triggers': ['__pregel_push'], 'langgraph_path': ('__pregel_push', ('__pregel_pull', 'main_workflow'), 1), 'langgraph_checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_fun_fact:39877473-4a1c-7791-5576-966d938afda4', 'checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_fun_fact:39877473-4a1c-7791-5576-966d938afda4', 'ls_provider': 'google_genai', 'ls_model_name': 'models/gemini-2.0-flash-exp', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['f2ab0f1b-77ed-408e-bb14-df4da93c6ea0', 'cdcc156c-ef1b-4cbb-bfaa-66b731e75312', 'e2408d04-634d-48cd-b417-a122298f7cb8']}\n",
            "Generated fun fact: {'event': 'on_chat_model_stream', 'data': {'chunk': AIMessageChunk(content=':** It\\'s a good example of a design choice that might seem counterintuitive at first glance, highlighting how programming languages can sometimes have clever under-the-hood mechanisms.\\n\\nSo, \"None\" isn\\'t just an absence; it\\'s a special, singular, and very particular absence!', additional_kwargs={}, response_metadata={'finish_reason': 'STOP', 'safety_ratings': [{'category': 'HARM_CATEGORY_HATE_SPEECH', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HARASSMENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT', 'probability': 'NEGLIGIBLE', 'blocked': False}]}, id='run-2549829c-dd09-4d93-8644-8f82a58000ac', usage_metadata={'input_tokens': -1, 'output_tokens': 255, 'total_tokens': 254, 'input_token_details': {'cache_read': 0}})}, 'run_id': '2549829c-dd09-4d93-8644-8f82a58000ac', 'name': 'ChatGoogleGenerativeAI', 'tags': ['seq:step:1'], 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'langgraph_step': 4, 'langgraph_node': 'generate_fun_fact', 'langgraph_triggers': ['__pregel_push'], 'langgraph_path': ('__pregel_push', ('__pregel_pull', 'main_workflow'), 1), 'langgraph_checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_fun_fact:39877473-4a1c-7791-5576-966d938afda4', 'checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_fun_fact:39877473-4a1c-7791-5576-966d938afda4', 'ls_provider': 'google_genai', 'ls_model_name': 'models/gemini-2.0-flash-exp', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['f2ab0f1b-77ed-408e-bb14-df4da93c6ea0', 'cdcc156c-ef1b-4cbb-bfaa-66b731e75312', 'e2408d04-634d-48cd-b417-a122298f7cb8']}\n",
            "Generated fun fact: {'event': 'on_chat_model_end', 'data': {'output': AIMessage(content='Okay, here\\'s a fun fact about \"None\" (in the programming sense, particularly in Python, but the concept applies more broadly):\\n\\n**Fun Fact:**  While \"None\" represents the absence of a value, it\\'s actually a *singleton object*. This means that in Python (and similar languages), there\\'s only *one* instance of `None` in the entire program.  Every time you use `None`, you\\'re referring to that exact same object in memory. You can even test this with the `is` operator: `None is None` will always evaluate to `True`.\\n\\n**Why is this fun?**\\n\\n* **It\\'s a unique individual:** Even though it represents nothing, it\\'s a very specific thing.  \\n* **Memory Efficiency:**  It\\'s more memory-efficient than creating a new \"nothing\" object each time you need one.\\n* **A subtle quirk:** It\\'s a good example of a design choice that might seem counterintuitive at first glance, highlighting how programming languages can sometimes have clever under-the-hood mechanisms.\\n\\nSo, \"None\" isn\\'t just an absence; it\\'s a special, singular, and very particular absence!', additional_kwargs={}, response_metadata={'safety_ratings': [{'category': 'HARM_CATEGORY_HATE_SPEECH', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HARASSMENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HATE_SPEECH', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HARASSMENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HATE_SPEECH', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HARASSMENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HATE_SPEECH', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HARASSMENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HATE_SPEECH', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HARASSMENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HATE_SPEECH', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HARASSMENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HATE_SPEECH', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HARASSMENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT', 'probability': 'NEGLIGIBLE', 'blocked': False}], 'finish_reason': 'STOP'}, id='run-2549829c-dd09-4d93-8644-8f82a58000ac', usage_metadata={'input_tokens': 7, 'output_tokens': 255, 'total_tokens': 262, 'input_token_details': {'cache_read': 0}}), 'input': {'messages': [[HumanMessage(content='Tell me a fun fact about None', additional_kwargs={}, response_metadata={})]]}}, 'run_id': '2549829c-dd09-4d93-8644-8f82a58000ac', 'name': 'ChatGoogleGenerativeAI', 'tags': ['seq:step:1'], 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'langgraph_step': 4, 'langgraph_node': 'generate_fun_fact', 'langgraph_triggers': ['__pregel_push'], 'langgraph_path': ('__pregel_push', ('__pregel_pull', 'main_workflow'), 1), 'langgraph_checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_fun_fact:39877473-4a1c-7791-5576-966d938afda4', 'checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_fun_fact:39877473-4a1c-7791-5576-966d938afda4', 'ls_provider': 'google_genai', 'ls_model_name': 'models/gemini-2.0-flash-exp', 'ls_model_type': 'chat', 'ls_temperature': 0.7}, 'parent_ids': ['f2ab0f1b-77ed-408e-bb14-df4da93c6ea0', 'cdcc156c-ef1b-4cbb-bfaa-66b731e75312', 'e2408d04-634d-48cd-b417-a122298f7cb8']}\n",
            "Generated fun fact: {'event': 'on_chain_start', 'data': {'input': 'Okay, here\\'s a fun fact about \"None\" (in the programming sense, particularly in Python, but the concept applies more broadly):\\n\\n**Fun Fact:**  While \"None\" represents the absence of a value, it\\'s actually a *singleton object*. This means that in Python (and similar languages), there\\'s only *one* instance of `None` in the entire program.  Every time you use `None`, you\\'re referring to that exact same object in memory. You can even test this with the `is` operator: `None is None` will always evaluate to `True`.\\n\\n**Why is this fun?**\\n\\n* **It\\'s a unique individual:** Even though it represents nothing, it\\'s a very specific thing.  \\n* **Memory Efficiency:**  It\\'s more memory-efficient than creating a new \"nothing\" object each time you need one.\\n* **A subtle quirk:** It\\'s a good example of a design choice that might seem counterintuitive at first glance, highlighting how programming languages can sometimes have clever under-the-hood mechanisms.\\n\\nSo, \"None\" isn\\'t just an absence; it\\'s a special, singular, and very particular absence!'}, 'name': '_write', 'tags': ['seq:step:2', 'langsmith:hidden'], 'run_id': 'd2ea0317-8d45-43e3-8e6a-549acb486c19', 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'langgraph_step': 4, 'langgraph_node': 'generate_fun_fact', 'langgraph_triggers': ['__pregel_push'], 'langgraph_path': ('__pregel_push', ('__pregel_pull', 'main_workflow'), 1), 'langgraph_checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_fun_fact:39877473-4a1c-7791-5576-966d938afda4'}, 'parent_ids': ['f2ab0f1b-77ed-408e-bb14-df4da93c6ea0', 'cdcc156c-ef1b-4cbb-bfaa-66b731e75312', 'e2408d04-634d-48cd-b417-a122298f7cb8']}\n",
            "Generated fun fact: {'event': 'on_chain_end', 'data': {'output': 'Okay, here\\'s a fun fact about \"None\" (in the programming sense, particularly in Python, but the concept applies more broadly):\\n\\n**Fun Fact:**  While \"None\" represents the absence of a value, it\\'s actually a *singleton object*. This means that in Python (and similar languages), there\\'s only *one* instance of `None` in the entire program.  Every time you use `None`, you\\'re referring to that exact same object in memory. You can even test this with the `is` operator: `None is None` will always evaluate to `True`.\\n\\n**Why is this fun?**\\n\\n* **It\\'s a unique individual:** Even though it represents nothing, it\\'s a very specific thing.  \\n* **Memory Efficiency:**  It\\'s more memory-efficient than creating a new \"nothing\" object each time you need one.\\n* **A subtle quirk:** It\\'s a good example of a design choice that might seem counterintuitive at first glance, highlighting how programming languages can sometimes have clever under-the-hood mechanisms.\\n\\nSo, \"None\" isn\\'t just an absence; it\\'s a special, singular, and very particular absence!', 'input': 'Okay, here\\'s a fun fact about \"None\" (in the programming sense, particularly in Python, but the concept applies more broadly):\\n\\n**Fun Fact:**  While \"None\" represents the absence of a value, it\\'s actually a *singleton object*. This means that in Python (and similar languages), there\\'s only *one* instance of `None` in the entire program.  Every time you use `None`, you\\'re referring to that exact same object in memory. You can even test this with the `is` operator: `None is None` will always evaluate to `True`.\\n\\n**Why is this fun?**\\n\\n* **It\\'s a unique individual:** Even though it represents nothing, it\\'s a very specific thing.  \\n* **Memory Efficiency:**  It\\'s more memory-efficient than creating a new \"nothing\" object each time you need one.\\n* **A subtle quirk:** It\\'s a good example of a design choice that might seem counterintuitive at first glance, highlighting how programming languages can sometimes have clever under-the-hood mechanisms.\\n\\nSo, \"None\" isn\\'t just an absence; it\\'s a special, singular, and very particular absence!'}, 'run_id': 'd2ea0317-8d45-43e3-8e6a-549acb486c19', 'name': '_write', 'tags': ['seq:step:2', 'langsmith:hidden'], 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'langgraph_step': 4, 'langgraph_node': 'generate_fun_fact', 'langgraph_triggers': ['__pregel_push'], 'langgraph_path': ('__pregel_push', ('__pregel_pull', 'main_workflow'), 1), 'langgraph_checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_fun_fact:39877473-4a1c-7791-5576-966d938afda4'}, 'parent_ids': ['f2ab0f1b-77ed-408e-bb14-df4da93c6ea0', 'cdcc156c-ef1b-4cbb-bfaa-66b731e75312', 'e2408d04-634d-48cd-b417-a122298f7cb8']}\n",
            "Generated fun fact: {'event': 'on_chain_stream', 'run_id': 'e2408d04-634d-48cd-b417-a122298f7cb8', 'name': 'generate_fun_fact', 'tags': ['seq:step:1'], 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'langgraph_step': 4, 'langgraph_node': 'generate_fun_fact', 'langgraph_triggers': ['__pregel_push'], 'langgraph_path': ('__pregel_push', ('__pregel_pull', 'main_workflow'), 1), 'langgraph_checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_fun_fact:39877473-4a1c-7791-5576-966d938afda4'}, 'data': {'chunk': 'Okay, here\\'s a fun fact about \"None\" (in the programming sense, particularly in Python, but the concept applies more broadly):\\n\\n**Fun Fact:**  While \"None\" represents the absence of a value, it\\'s actually a *singleton object*. This means that in Python (and similar languages), there\\'s only *one* instance of `None` in the entire program.  Every time you use `None`, you\\'re referring to that exact same object in memory. You can even test this with the `is` operator: `None is None` will always evaluate to `True`.\\n\\n**Why is this fun?**\\n\\n* **It\\'s a unique individual:** Even though it represents nothing, it\\'s a very specific thing.  \\n* **Memory Efficiency:**  It\\'s more memory-efficient than creating a new \"nothing\" object each time you need one.\\n* **A subtle quirk:** It\\'s a good example of a design choice that might seem counterintuitive at first glance, highlighting how programming languages can sometimes have clever under-the-hood mechanisms.\\n\\nSo, \"None\" isn\\'t just an absence; it\\'s a special, singular, and very particular absence!'}, 'parent_ids': ['f2ab0f1b-77ed-408e-bb14-df4da93c6ea0', 'cdcc156c-ef1b-4cbb-bfaa-66b731e75312']}\n",
            "Generated fun fact: {'event': 'on_chain_end', 'data': {'output': 'Okay, here\\'s a fun fact about \"None\" (in the programming sense, particularly in Python, but the concept applies more broadly):\\n\\n**Fun Fact:**  While \"None\" represents the absence of a value, it\\'s actually a *singleton object*. This means that in Python (and similar languages), there\\'s only *one* instance of `None` in the entire program.  Every time you use `None`, you\\'re referring to that exact same object in memory. You can even test this with the `is` operator: `None is None` will always evaluate to `True`.\\n\\n**Why is this fun?**\\n\\n* **It\\'s a unique individual:** Even though it represents nothing, it\\'s a very specific thing.  \\n* **Memory Efficiency:**  It\\'s more memory-efficient than creating a new \"nothing\" object each time you need one.\\n* **A subtle quirk:** It\\'s a good example of a design choice that might seem counterintuitive at first glance, highlighting how programming languages can sometimes have clever under-the-hood mechanisms.\\n\\nSo, \"None\" isn\\'t just an absence; it\\'s a special, singular, and very particular absence!', 'input': {'city': None}}, 'run_id': 'e2408d04-634d-48cd-b417-a122298f7cb8', 'name': 'generate_fun_fact', 'tags': ['seq:step:1'], 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'langgraph_step': 4, 'langgraph_node': 'generate_fun_fact', 'langgraph_triggers': ['__pregel_push'], 'langgraph_path': ('__pregel_push', ('__pregel_pull', 'main_workflow'), 1), 'langgraph_checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3|generate_fun_fact:39877473-4a1c-7791-5576-966d938afda4'}, 'parent_ids': ['f2ab0f1b-77ed-408e-bb14-df4da93c6ea0', 'cdcc156c-ef1b-4cbb-bfaa-66b731e75312']}\n",
            "Generated fun fact: {'event': 'on_chain_stream', 'run_id': 'f2ab0f1b-77ed-408e-bb14-df4da93c6ea0', 'name': 'LangGraph', 'tags': [], 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055'}, 'data': {'chunk': {'generate_fun_fact': 'Okay, here\\'s a fun fact about \"None\" (in the programming sense, particularly in Python, but the concept applies more broadly):\\n\\n**Fun Fact:**  While \"None\" represents the absence of a value, it\\'s actually a *singleton object*. This means that in Python (and similar languages), there\\'s only *one* instance of `None` in the entire program.  Every time you use `None`, you\\'re referring to that exact same object in memory. You can even test this with the `is` operator: `None is None` will always evaluate to `True`.\\n\\n**Why is this fun?**\\n\\n* **It\\'s a unique individual:** Even though it represents nothing, it\\'s a very specific thing.  \\n* **Memory Efficiency:**  It\\'s more memory-efficient than creating a new \"nothing\" object each time you need one.\\n* **A subtle quirk:** It\\'s a good example of a design choice that might seem counterintuitive at first glance, highlighting how programming languages can sometimes have clever under-the-hood mechanisms.\\n\\nSo, \"None\" isn\\'t just an absence; it\\'s a special, singular, and very particular absence!'}}, 'parent_ids': []}\n",
            "Generated fun fact: {'event': 'on_chain_start', 'data': {'input': {'city': None, 'fun_fact': None, 'country': 'Pakistan'}}, 'name': '_write', 'tags': ['seq:step:2', 'langsmith:hidden'], 'run_id': 'c729535e-00e6-42b3-8940-fbbf033265aa', 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'langgraph_step': 4, 'langgraph_node': 'main_workflow', 'langgraph_triggers': ['__start__'], 'langgraph_path': ('__pregel_pull', 'main_workflow'), 'langgraph_checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3'}, 'parent_ids': ['f2ab0f1b-77ed-408e-bb14-df4da93c6ea0', 'cdcc156c-ef1b-4cbb-bfaa-66b731e75312']}\n",
            "Generated fun fact: {'event': 'on_chain_end', 'data': {'output': {'city': None, 'fun_fact': None, 'country': 'Pakistan'}, 'input': {'city': None, 'fun_fact': None, 'country': 'Pakistan'}}, 'run_id': 'c729535e-00e6-42b3-8940-fbbf033265aa', 'name': '_write', 'tags': ['seq:step:2', 'langsmith:hidden'], 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'langgraph_step': 4, 'langgraph_node': 'main_workflow', 'langgraph_triggers': ['__start__'], 'langgraph_path': ('__pregel_pull', 'main_workflow'), 'langgraph_checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3'}, 'parent_ids': ['f2ab0f1b-77ed-408e-bb14-df4da93c6ea0', 'cdcc156c-ef1b-4cbb-bfaa-66b731e75312']}\n",
            "Generated fun fact: {'event': 'on_chain_stream', 'run_id': 'cdcc156c-ef1b-4cbb-bfaa-66b731e75312', 'name': 'main_workflow', 'tags': ['graph:step:4'], 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'langgraph_step': 4, 'langgraph_node': 'main_workflow', 'langgraph_triggers': ['__start__'], 'langgraph_path': ('__pregel_pull', 'main_workflow'), 'langgraph_checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3'}, 'data': {'chunk': {'city': None, 'fun_fact': None, 'country': 'Pakistan'}}, 'parent_ids': ['f2ab0f1b-77ed-408e-bb14-df4da93c6ea0']}\n",
            "Generated fun fact: {'event': 'on_chain_end', 'data': {'output': {'city': None, 'fun_fact': None, 'country': 'Pakistan'}, 'input': 'Pakistan'}, 'run_id': 'cdcc156c-ef1b-4cbb-bfaa-66b731e75312', 'name': 'main_workflow', 'tags': ['graph:step:4'], 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055', 'langgraph_step': 4, 'langgraph_node': 'main_workflow', 'langgraph_triggers': ['__start__'], 'langgraph_path': ('__pregel_pull', 'main_workflow'), 'langgraph_checkpoint_ns': 'main_workflow:96ae4f09-afd5-10c5-fed7-a2d21e4555c3'}, 'parent_ids': ['f2ab0f1b-77ed-408e-bb14-df4da93c6ea0']}\n",
            "Generated fun fact: {'event': 'on_chain_stream', 'run_id': 'f2ab0f1b-77ed-408e-bb14-df4da93c6ea0', 'name': 'LangGraph', 'tags': [], 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055'}, 'data': {'chunk': {'main_workflow': {'city': None, 'fun_fact': None, 'country': 'Pakistan'}}}, 'parent_ids': []}\n",
            "Generated fun fact: {'event': 'on_chain_end', 'data': {'output': {'city': None, 'fun_fact': None, 'country': 'Pakistan'}}, 'run_id': 'f2ab0f1b-77ed-408e-bb14-df4da93c6ea0', 'name': 'LangGraph', 'tags': [], 'metadata': {'thread_id': 'd6059393-0643-4560-9670-f602016d8055'}, 'parent_ids': []}\n"
          ]
        }
      ]
    }
  ],
  "metadata": {
    "colab": {
      "provenance": [],
      "collapsed_sections": [
        "UbwszHTkPshP"
      ]
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}